// Copyright (C) 2007 Gameloft. All rights reserved.
// GAMELOFT PROPRIETARY/CONFIDENTIAL.
//
// Author: balife

#include "Display.h"
#include "../PreHeader.h"
#include "Graphics.h"
#include "IPLWrappingFunctions.h"

class CGame;
extern CGame* gp_GameApp;

CRect _currentClip(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
CRect _lastClip(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);

#ifdef WIN32
#define glClipPlanef glClipPlane
#else // iPhone
#include "../iphone/GamePlatformIP.h"
#endif

	cGfx*		gScreenGraphics = NULL;
	int				gBGOffset[2][4][2];
	bool			gbHasDrawBG[2] = {false, false};
	//int gScreenBrightness = 0;
	bool			gbFlipScreen = false;
	int				gAlpha2D = 31;
	int				gAlpha3D = 31;
	GLint gPrevMatrixMode;

	typedef struct {
		GLfloat pos[3];
		GLfloat vel[3];
		GLfloat acc[3];
		GLfloat rot[3];
	} Camera;

	Camera gCamera;
	const GLfloat CAMERA_FOVY = IPLMath_IntToFixed(45);
	const GLfloat CAMERA_ASPECT = IPLMath_FloatToFixed( G_CAMERA_ASPECT );//IPLMath_FixedDiv(IPLMath_IntToFixed(320), IPLMath_IntToFixed(240));
	const GLfloat CAMERA_NEARZ = IPLMath_IntToFixed(0);
	const GLfloat CAMERA_FARZ = IPLMath_IntToFixed(120);

	const GLfloat Camera_Mode[][4] = {
		{ IPLMath_IntToFixed(45), IPLMath_FloatToFixed( G_CAMERA_ASPECT ), IPLMath_IntToFixed(0), IPLMath_IntToFixed(120) },
		{ IPLMath_IntToFixed(60), IPLMath_FixedDiv((int) IPLMath_IntToFixed(320), (int) IPLMath_IntToFixed(240)), 1, IPLMath_IntToFixed(10) }
	};

	typedef struct
	{
		//Bitmap*			pBitmap;
		//CachedBitmap*	pCachedBitmap;
		int				dx;
		int				dy;
		T_F32			sx;
		T_F32			sy;
		int				tx;//for rotation only
		int				ty;//for rotation only
		int				rot;//for rotation only
		int				flag;
		int				alpha;
		int             priority;
	}RenderInfo;

	ObjCommonRenderInfo g2DCommRI;	
	ObjCommonRenderInfo g3DCommRI;
	ObjRenderQueueInfo gRenderQI;

	//vector<RenderInfo> g2DRenderTask;
	//vector<RenderInfo> g3DRenderTask;

	#define RENDER_TASK_MAX		4096

	RenderInfo g2DRenderTask[RENDER_TASK_MAX];
	RenderInfo g3DRenderTask[RENDER_TASK_MAX];
	//int		   g2DRenderTaskCount = 0;
	//int		   g3DRenderTaskCount = 0;

	//int  gPriority = G2D_PRIORIY_LOW;

	//void GFX_SetG2DPriority(int priority)
	//{
	//	gPriority = priority;
	//}

	//void GFX_ResetG2dPriority()
	//{
	//	gPriority = G2D_PRIORIY_LOW;
	//}

	void GFX_Initialize()
	{
		// RUIZD: The following block is the original code for OpenGL initialization
		// Set up a default orthographic projection matrix and set the model matrix to the identity
		//IPLGLMatrixStack_MatrixMode( IPL_MSE_PROJECTION );
		//IPLGLMatrixStack_LoadMatrixx( IPL_STANDARD_ORTHO_MATRIX );
		//IPLGLMatrixStack_MatrixMode( IPL_MSE_MODELVIEW );
		//IPLGLMatrixStack_LoadIdentity();

#ifdef WIN32
	glShadeModel(GL_SMOOTH);							// Enable Smooth Shading
	
	// Culling
	// RUIZD: Don't apply culling right now
    //glCullFace(GL_BACK);
    //glFrontFace(GL_CCW);
    //glEnable(GL_CULL_FACE);

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

	glEnable(GL_DEPTH_TEST);							// Enables Depth Testing
	glDepthFunc(GL_LEQUAL);								// The Type Of Depth Testing To Do
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);	// Really Nice Perspective Calculations

	glViewport(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);						// Reset The Current Viewport

	// Select the MODELVIEW matrix
	//glMatrixMode(GL_MODELVIEW);							
	//glLoadIdentity();	

	// Select the PROJECTION matrix
	glMatrixMode(GL_PROJECTION);						// Select The Projection Matrix
	glLoadIdentity();									// Reset The Projection Matrix

	// Calculate The Aspect Ratio Of The Window
	glOrtho(-(SCREEN_WIDTH >> 1), SCREEN_WIDTH >> 1, -(SCREEN_HEIGHT >> 1), SCREEN_HEIGHT >> 1, -500.0f, 500.0f);
	GFX_Clear(0xffffffff);
#else // iPhone

	// RUIZD: From GamePlatformIP
	//-AC_: Not uing anymore
	GameIPhoneStartup();
	GameIPhoneSetupOpenGL();
	
#endif

	}

	/*void GFX_Release()
	{
		g2DRenderTaskCount = 0;
		g3DRenderTaskCount = 0;
	}*/

	/*void* GFX_GetGraphics()
	{
		return gScreenGraphics;
	}

	void GFX_SetBrightness(int value)
	{
		gScreenBrightness = 256 + value;
	}*/

	void GFX_Clear(int col)
	{
		T_F32 r = (T_F32)((col & 0x00ff0000) >> 16) / 256;
		T_F32 g = (T_F32)((col & 0x0000ff00) >>  8) / 256;
		T_F32 b = (T_F32)((col & 0x000000ff)      ) / 256;
		glClearColor( r, g, b, 1.f);
		//glClear(GL_COLOR_BUFFER_BIT/* | GL_DEPTH_BUFFER_BIT*/);
		glClear(GL_COLOR_BUFFER_BIT /*| GL_DEPTH_BUFFER_BIT*/);

		IPLGLStateVector_SetCurrentToBuiltin( IPL_BSV_UNIFORMBLENDING );
	}

	typedef struct{
		u8 b;
		u8 g;
		u8 r;
		u8 a;
	}ARGBColor;


	void GFX_SetClip(int x, int y, int width, int height)
	{		
		_lastClip = _currentClip;

		_currentClip.left = x;
		_currentClip.top = y;
		_currentClip.right = x + width;
		_currentClip.bottom = y + height;

		y += 2;
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		glOrtho(x, width + x, SCREEN_HEIGHT - (y + height), SCREEN_HEIGHT - y, -500.0f, 500.0f);
		glViewport(x, SCREEN_HEIGHT - (y + height), width, height);
	}

	void GFX_ClipRect(int x, int y, int width, int height)
	{		
		_lastClip = _currentClip;

		_currentClip.left = x;
		_currentClip.top = y;
		_currentClip.right = x + width;
		_currentClip.bottom = y + height;
		_currentClip = _currentClip.Intersect(_lastClip);

		x = _currentClip.left;
		y = _currentClip.top;
		width = _currentClip.GetWidth();
		height = _currentClip.GetHeight();

		y += 2;
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		glOrtho(x, width + x, SCREEN_HEIGHT - (y + height), SCREEN_HEIGHT - y, -500.0f, 500.0f);
		glViewport(x, SCREEN_HEIGHT - (y + height), width, height);
	}

	void GFX_SetLastClip()
	{		
		CRect temp = _currentClip;

		_currentClip = _lastClip;
		_lastClip = temp;

		int x = _currentClip.left;
		int y = _currentClip.top;
		int width = _currentClip.GetWidth();
		int height = _currentClip.GetHeight();

		y += 2;
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		glOrtho(x, width + x, SCREEN_HEIGHT - (y + height), SCREEN_HEIGHT - y, -250.0f, 250.0f);
		glViewport(x, SCREEN_HEIGHT - (y + height), width, height);
	}

	void GFX_ResetClip()
	{
		_lastClip = _currentClip;

		_currentClip.left = 0;
		_currentClip.top = 0;
		_currentClip.right = SCREEN_WIDTH;
		_currentClip.bottom = SCREEN_HEIGHT;

		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		glOrtho(0, SCREEN_WIDTH, 0, SCREEN_HEIGHT, -250.0f, 250.0f);
		glViewport(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
	}

	void GFX_ResetSprCommRI()
	{
		g2DCommRI.oX_if = 0;
		g2DCommRI.oY_if = 0;
		g2DCommRI.oX_is = 0;
		g2DCommRI.oY_is = 0;
		g2DCommRI.scaleX = 1.0f;
		g2DCommRI.scaleY = 1.0f;
		g2DCommRI.rot = 0;
		g2DCommRI.transform = IS_NONE;
		g2DCommRI.anchorType = Is_FrameCenter;
		//g2DCommRI.texIdx = -1;
		//g2DCommRI.stateVector = -1;
	}
	
	void GFX_SetScale(T_F32 x, T_F32 y, int ax, int ay)
	{		
		g2DCommRI.scaleX = IPLMath_FloatToFixed(x);
		g2DCommRI.scaleY = IPLMath_FloatToFixed(y);
		/*if( (g2DCommRI.scaleX == 1.0f) && (g2DCommRI.scaleX == 1.0f) )
		{
			GFX_ResetScale();
			return;
		}*/
		g2DCommRI.oX_is = ax;
		g2DCommRI.oY_is = ay;
		g2DCommRI.transform |= IS_SCALE;
		if( (ax != MAGIC_NUM) && (ay != MAGIC_NUM) )
			g2DCommRI.anchorType = Is_MultiFrameCenter;
	}
	void GFX_ResetScale()
	{
		g2DCommRI.scaleX = 1.0f;
		g2DCommRI.scaleY = 1.0f;
		g2DCommRI.transform &= ~IS_SCALE;
		g2DCommRI.anchorType = Is_FrameCenter;
	}
	void GFX_SetRotate(T_F32 angle, char isXYZ)
	{
		/*if( angle == 0.0f )
		{
			GFX_ResetRotate();
			return;
		}*/
		g2DCommRI.rot = IPLMath_FloatToFixed(angle);
		g2DCommRI.transform |= isXYZ;
	}
	void GFX_ResetRotate()
	{
		g2DCommRI.rot = 0;
		g2DCommRI.transform &= ~IS_ROT;
		g2DCommRI.transform &= ~IS_X_ROT;
		g2DCommRI.transform &= ~IS_Y_ROT;
		g2DCommRI.transform &= ~IS_Z_ROT;
		//g2DCommRI.transform = IS_NONE;
	}

#ifdef _OptimizePaintFrame
#else
	void GFX_AdjustAnchor(int ax, int ay)
	{
		g2DCommRI.oX_is += ax;
		g2DCommRI.oY_is += ay;
	}
	void GFX_AdjustAnchor(int anchorType)
	{
		g2DCommRI.anchorType = anchorType;
	}
	void GFX_SetAnchor(int ax, int ay, int type)
	{
		if(type==anchor_frm)
		{
			g2DCommRI.oX_if = ax;
			g2DCommRI.oY_if = ay;
		}
		else
		{
			g2DCommRI.oX_is = ax;
			g2DCommRI.oY_is = ay;
		}
	}
	void GFX_ClearAnchor(int type)
	{		
		if(type==anchor_frm)
		{
			g2DCommRI.oX_if = 0;
			g2DCommRI.oY_if = 0;
		}
		else
		{
			g2DCommRI.oX_is = 0;
			g2DCommRI.oY_is = 0;
		}
	}
#endif

	///*void InsertSort(RenderInfo* task, int& size)
	//{
	//	int index = 0;
	//	do
	//	{
	//		if(++index >= size)
	//			return;

	//		int subIndex = index;
	//		while(--subIndex >= 0)
	//		{
	//			if(task[subIndex].priority >= task[index].priority || (subIndex == 0 && index != 1))
	//			{
	//				if(subIndex + 1 == index)
	//					break;

	//				int tempIndex = index;
	//				RenderInfo temp = task[index];

	//				if(subIndex == 0 && task[subIndex].priority < task[index].priority)
	//					0;
	//				else
	//					subIndex++;

	//				while(tempIndex > subIndex)
	//				{
	//					task[tempIndex] = task[tempIndex - 1];
	//					tempIndex--;
	//				}
	//				task[tempIndex] = temp;
	//				break;
	//			}

	//		}
	//	} while(true);
	//}*/

	void GFX_2DBeginRendering()
	{
		// Save and set matrices
		IPLGLMatrixStack_GetIntegerv( IPL_MSE_MATRIX_MODE, &gPrevMatrixMode );

		IPLGLMatrixStack_MatrixMode(IPL_MSE_PROJECTION);
		IPLGLMatrixStack_PushMatrix();
		IPLGLMatrixStack_LoadMatrixx(IPL_STANDARD_ORTHO_MATRIX);

		IPLGLMatrixStack_MatrixMode(IPL_MSE_MODELVIEW);
		IPLGLMatrixStack_PushMatrix();
		IPLGLMatrixStack_LoadIdentity();
	}
	void GFX_2DEndRendering()
	{
		// Restore the matrix stack to its previous state
		IPLGLMatrixStack_PopMatrix();
		IPLGLMatrixStack_MatrixMode( IPL_MSE_PROJECTION );
		IPLGLMatrixStack_PopMatrix();
		IPLGLMatrixStack_MatrixMode(gPrevMatrixMode);
	}
	void GFX_3DBeginRendering()
	{
		// Save and set matrices
		IPLGLMatrixStack_GetIntegerv( IPL_MSE_MATRIX_MODE, &gPrevMatrixMode );

		IPLGLMatrixStack_MatrixMode( IPL_MSE_PROJECTION );
		IPLGLMatrixStack_PushMatrix();
		IPLGLMatrixStack_LoadIdentity();
		//IPLGLUtil_Perspectivex(CAMERA_FOVY, CAMERA_ASPECT, CAMERA_NEARZ, CAMERA_FARZ);
		IPLGLMatrixStack_Perspectivex( Camera_Mode[0][0], Camera_Mode[0][1], Camera_Mode[0][2], Camera_Mode[0][3] );

		IPLGLMatrixStack_MatrixMode(IPL_MSE_MODELVIEW);
		IPLGLMatrixStack_PushMatrix();
		IPLGLMatrixStack_LoadIdentity();

		// Enable vertex pos & texture coords
		//glEnableVertexAttribArray(0);
		//glEnableVertexAttribArray(1);

		// Set up initial camera settings
		gCamera.pos[0] = 0;
		gCamera.pos[1] = 0;//IPLMath_FloatToFixed(-4.f);
		gCamera.pos[2] = 0;

		gCamera.vel[0] = 0;
		gCamera.vel[1] = 0;
		gCamera.vel[2] = 0;//IPLMath_FloatToFixed(-10.f);

		gCamera.acc[0] = 0;
		gCamera.acc[1] = 0;
		gCamera.acc[2] = 0;

		gCamera.rot[0] = 0;//IPLMath_FloatToFixed(-5.f);
		gCamera.rot[1] = 0;
		gCamera.rot[2] = 0;

		//IPLGLMatrixStack_MatrixMode( IPL_MSE_MODELVIEW );
	}
	void GFX_3DEndRendering()
	{
		//glDisableVertexAttribArray(1);
		//glDisableVertexAttribArray(0);

		// Restore the matrix stack to its previous state
		IPLGLMatrixStack_PopMatrix();
		IPLGLMatrixStack_MatrixMode( IPL_MSE_PROJECTION );
		IPLGLMatrixStack_PopMatrix();
		IPLGLMatrixStack_MatrixMode(gPrevMatrixMode);
	}

	/*void GFX_Reset()
	{
	}

	void GFX_Begin()
	{
	}*/

//	CRect ScreenRect(UPPER_SCREEN_LEFT, UPPER_SCREEN_TOP, TOTAL_SCREEN_WIDTH, TOTAL_SCREEN_HEIGHT);

	//typedef struct
	//{
	//	unsigned char b;
	//	unsigned char g;
	//	unsigned char r;
	//	unsigned char a;
	//}ScreenColor;

	/*void GFX_AdjustBrightness()
	{
	}*/


	/*void GFX_End()
	{
	}*/

	/*void GFX_SetAlpha3D(int a)
	{
		gAlpha3D = a & 0x1F;
	}

	void GFX_ResetAlpha3D()
	{
		gAlpha3D = 31;
	}*/

	/*void GFX_SetScreenFlip(bool flip)
	{
		gbFlipScreen = flip;
	}*/

	/*bool GFX_GetScreenFlip()
	{
		return gbFlipScreen;
	}*/

	//int gDisplayMode = 0;

	/*void GFX_SetupDisplayMode(int id_)
	{

	}*/

	bool _clipEnabled;
	GLfloat _clipPlane;
	void GFX_3DSetClipPlane(bool enable, GLfloat value)
	{
		_clipEnabled = enable; 
		_clipPlane = value;
	}


	int GFX_BlendColors(int c1, int c2, int blend)
	{
	  int cr = 0;
	  int cg = 0;
	  int cb = 0;
	  int cr1 = (c1 & 0xFF0000) >> 16;
	  int cg1 = (c1 & 0x00FF00) >> 8;
	  int cb1 = c1 & 0x0000FF;
	  int cr2 = (c2 & 0xFF0000) >> 16;
	  int cg2 = (c2 & 0x00FF00) >> 8;
	  int cb2 = c2 & 0x0000FF;

	  cr = (cr1 * (100 - blend) + (cr2 * blend)) / 100;
	  cg = (cg1 * (100 - blend) + (cg2 * blend)) / 100;
	  cb = (cb1 * (100 - blend) + (cb2 * blend)) / 100;

	  cr = (cr < 0) ? 255 : cr;
	  cg = (cg < 0) ? 255 : cg;
	  cb = (cb < 0) ? 255 : cb;

	  cr = (cr > 255) ? 255 : cr;
	  cg = (cg > 255) ? 255 : cg;
	  cb = (cb > 255) ? 255 : cb;

	  return (0xff << 24) | (cr << 16) | (cg << 8) | cb;
	}

#define ORTHO_VIEW_LEFT			-160.0f
#define ORTHO_VIEW_RIGHT		 160.0f
#define ORTHO_VIEW_BOTTOM		-120.0f
#define ORTHO_VIEW_TOP			 120.0f
#define ORTHO_VIEW_NEAR			 -500.0f
#define ORTHO_VIEW_FAR			 500.0f
	//GLfloat g3DViewbase = 160.0f;
	void Begin3D(bool _flipOrthoX)
	{
		//------------------------ Save current matrix stack --------------------------
		//GLint matrix_mode;
		glGetIntegerv( IPL_MSE_MATRIX_MODE, &gPrevMatrixMode );
//		IPLGLUtil_PushMatrix();
		//IPLGLUtil_MatrixMode(gPrevMatrixMode == IPL_MSE_PROJECTION ? IPL_MSE_MODELVIEW : IPL_MSE_PROJECTION);		

		//--------------------------------- Render Scene ------------------------------
		//IPLGLUtil_PushMatrix();
		glMatrixMode(IPL_MSE_PROJECTION);
		
		glPushMatrix();
		glLoadIdentity();

		IPLGLMatrixStack_Orthof(ORTHO_VIEW_LEFT, ORTHO_VIEW_RIGHT, ORTHO_VIEW_BOTTOM, ORTHO_VIEW_TOP, ORTHO_VIEW_NEAR, ORTHO_VIEW_FAR);

		glMatrixMode(IPL_MSE_MODELVIEW);
		glPushMatrix();
		glLoadIdentity();
		//glClear( GL_DEPTH_BUFFER_BIT );

		if(_clipEnabled)
		{
#if defined WIN32
			GLdouble eqn[4] = {0.0f, 1.0f, 0.0f, _clipPlane};
#else
			GLfloat eqn[4] = {0.0f, 1.0f, 0.0f, _clipPlane};
#endif
			glClipPlanef(GL_CLIP_PLANE0, eqn);
			glEnable(GL_CLIP_PLANE0);
		}
	}
	void End3D()
	{
		glDisable(GL_CLIP_PLANE0);
		glPopMatrix();
		glMatrixMode(IPL_MSE_PROJECTION);
		glPopMatrix();
		glMatrixMode(gPrevMatrixMode);
	}